﻿(*                  FnSharp              

   Base class library extensions and frameworks aimed 
   specifically at improving F# developers lives!

   Written by: Huw Simpson
   License: Microsoft Public License (Ms-PL) 
   Available at: http://fnsharp.codeplex.com

*)
namespace FnSharp.Bcl.Core

    open System
    open System.Diagnostics

    [<DebuggerDisplay ("{Value}")>]
    type backingField<'T> internal (?writeLimit) =
        let mutable locked = false
        let mutable writeCount = 0
        let mutable defVl:'T option = None
        let mutable value:'T option = None

        let checkWrite () =
            if locked then failwith "Cannot overwrite a locked instance!"
            if writeLimit.IsSome && writeLimit.Value <= writeCount then failwith "Cannot overwrite, the write limit has been exceeded!"

        member self.Lock () =
            locked <- true

        member self.DefaultTo vl =
            match defVl with
            | None -> defVl <- Some vl
            | Some _ -> failwith "Default value already set!"

        member self.Set v =
            checkWrite ()
            defVl <- None //Don't need the default anymore
            value <- Some v            

        member self.Value =
            match defVl,value with
            | Some v,None -> v  //Defaulting
            | _,Some v -> v     //Not defaulting
            | _,_ -> failwith "Unable to default, no default was set!"

    ///TODO: Implement Comparison etc.
    [<RequireQualifiedAccess>]
    [<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
    module BackingField =
        let create<'T> = new backingField<'T> ()

        let createWriteOnce<'T> = new backingField<'T> (1)

        let createWrite<'T> n = new backingField<'T> (n)

        let defaultTo value (backingField:backingField<_>) = 
            backingField.DefaultTo (value)
            backingField

        let inline lock (backingField:backingField<_>) = backingField.Lock ()

        let inline get (backingField:backingField<_>) = backingField.Value

        let inline set value (backingField:backingField<_>) = backingField.Set (value)